package sim1011.poltergeist;

import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;

import sim1011.components.Component;
import sim1011.starter.EctoMobileStarter;
import sim1011.starter.Logger;
import sim1011.status.Status;

public class BatteryArbiter extends Arbiter implements java.io.Serializable {
	private static final float BATTERY_FOR_MEMORY_REQ = 0.002f;
	private static final float BATTERY_FOR_OPERATION_CPU = 0.01f;
	private static final float BATTERY_FOR_TRANSFER = 0.06f;
	private static final long serialVersionUID = 5397134689848309524L;
	protected float cost;
	private HashMap<String, Float> costRank = new HashMap<String, Float>();
	protected HashMap<String, Float> finalRank;
	private ArrayList<String> meanFreeCPU;
	private ArrayList<String> meanFreeMemories;
	private Status status;
	
	public BatteryArbiter(Status s, float cost) {
		this.cost = cost;
		this.status = s;		
	}

	@Override
	public void makeYourDecision(String component, String curContainer,
			ArrayList<String> availableNodes, HashMap<String, Float> finalRank) {
		this.finalRank = finalRank;
		if (curContainer.equals("Node.M")) {
			float max_score = 0.0f;
			float min_score = Float.MAX_VALUE;
			HashMap<String, Float> scores = new HashMap<String, Float>();
			Logger.logPolicy("[BATTERY ARBITER] availableNodes size: " + availableNodes.size());
			for (String nodeName : availableNodes) {
				float score = evaluateScore(nodeName, component);
				scores.put(nodeName, score);
				/*if (score < min_score)
					min_score = score;
				if (score > max_score)
					max_score = score;*/
			}
			Iterator<String> iter = scores.keySet().iterator();
			while (iter.hasNext()) {
				String nodeName = iter.next();
//				finalRank.put(
//						nodeName,
//						finalRank.get(nodeName) + this.cost * (EctoMobileStarter.MAX_RANK_VAL - Arbiter.range(max_score, min_score,
//												scores.get(nodeName))));
//				Logger.logPolicy("[BATTERY ARBITER] Range: " + Arbiter.range(max_score, min_score,
//												scores.get(nodeName)) + " for node " + nodeName);
				float consume = ((scores.get(nodeName)/EctoMobileStarter.MAX_COMP_SIZE_ON_MOBILE)*100);
				finalRank.put(
						nodeName,
						finalRank.get(nodeName) + this.cost * (EctoMobileStarter.MAX_RANK_VAL - consume));
			}
		} else {
			for(String nodeName : availableNodes) {
				if(nodeName.equals("Node.M"))
					finalRank.put(nodeName, finalRank.get(nodeName)+this.cost*0.0f);
				else finalRank.put(nodeName, finalRank.get(nodeName)+this.cost*EctoMobileStarter.MAX_RANK_VAL);
			}	
		}
	}
	
	private float evaluateScore(String containerID, String componentName) {
		int requiredMemory = status.getComponentsRequiredMemory().get(componentName).intValue();
		int requiredCPU = status.getComponentsRequiredCPU().get(componentName).intValue();
		if(containerID.equals("Node.M")) {
			//Logger.log("[BATTERYARBITER] Lo score per il nodo mobile �: " + batteryConsumption(requiredCPU, requiredMemory, 0));
			return batteryConsumption(requiredCPU, requiredMemory, 0);
		} else {
			int QoS = status.getNetworkMatrix().getLinkQoS(containerID, "Node.M");
			//Logger.log("[BATTERYARBITER] Lo score per il nodo " + containerID + " con QoS "+ QoS +" �: " + batteryConsumption(0, 0, QoS));
			return batteryConsumption(requiredCPU, requiredMemory, QoS);
		}
	}
	
	private float batteryConsumption(int requiredCPU, int requiredMemory,
			int QoS) {
		if(QoS > 0) {
			float newQoS = 1 + ((QoS-1)/11);
			float consume = ((EctoMobileStarter.BATTERY_FOR_TRANSFER/(newQoS)) * (Component
					.size(requiredCPU, requiredMemory*2) + EctoMobileStarter.MIGRATION_OVERHEAD));
			//Logger.logPolicy("[BATTERY ARBITER] Consume: " + consume + " QoS: " + QoS + " reqCPU " +requiredCPU+" reqMem: " +requiredMemory );
			return consume;
		} else return (requiredCPU * EctoMobileStarter.BATTERY_FOR_OPERATION_CPU + requiredMemory * EctoMobileStarter.BATTERY_FOR_MEMORY_REQ);
	}
}
